package fileutil

import (
	"bufio"
	"crypto/sha256"
	"fmt"
	"io"
	"io/fs"
	"os"
	"path/filepath"
	"strings"
)

func CopyFile(source string, target string) error {
	var err error
	var sourceFile *os.File
	var targetFile *os.File
	var sourceInfo os.FileInfo

	if sourceFile, err = os.Open(source); err != nil {
		return err
	} else {
		defer func() {
			// ignore err
			_ = sourceFile.Close()
		}()
	}
	if targetFile, err = os.Create(target); err != nil {
		return err
	} else {
		defer func() {
			// ignore err
			_ = targetFile.Close()
		}()
	}

	// do copy
	if _, err = io.Copy(targetFile, sourceFile); err != nil {
		return err
	}
	// chmod
	if sourceInfo, err = os.Stat(source); err != nil {
		return err
	} else {
		return os.Chmod(target, sourceInfo.Mode())
	}
}

func CopyDirectory(source string, target string) error {
	if sourceInfo, err := os.Stat(source); err != nil {
		return err
	} else {
		// mkdir
		if err = os.MkdirAll(target, sourceInfo.Mode()); err != nil {
			return err
		}
	}

	// walk impl
	err := filepath.Walk(source, func(from string, info fs.FileInfo, err error) error {
		to := strings.Replace(from, source, target, -1)
		if info.IsDir() {
			if mkdirErr := os.MkdirAll(to, info.Mode()); err != nil {
				return mkdirErr
			}
		} else {
			if copyErr := CopyFile(from, to); err != nil {
				return copyErr
			}
		}
		return err
	})
	if err != nil {
		return err
	}
	return nil
}

func ChecksumForFile(source string) (string, error) {
	var checksum string = ""
	var gErr error = nil
	ch := make(chan int)
	go func() {
		if _, err := os.Stat(source); err != nil {
			gErr = err
		} else {
			h := sha256.New()
			readByte, readErr := os.Open(source)
			defer func() {
				// ignore err
				_ = readByte.Close()
			}()
			if readErr != nil {
				gErr = readErr
			}
			if _, copyErr := io.Copy(h, readByte); copyErr == nil {
				sum := h.Sum(nil)
				checksum = fmt.Sprintf("%x", sum)
			} else {
				gErr = copyErr
			}
		}
		ch <- 1
	}()
	<-ch
	return checksum, gErr
}

func ChecksumForByte(source []byte) (string, error) {
	var checksum string = ""
	var gErr error = nil
	ch := make(chan int)
	go func() {
		h := sha256.New()
		if _, err := h.Write(source); err != nil {
			gErr = err
		} else {
			sum := h.Sum(nil)
			checksum = fmt.Sprintf("%x", sum)
		}
		ch <- 1
	}()
	<-ch
	return checksum, gErr
}

// OverwriteWrite
// overwrite write file, abort old content
func OverwriteWrite(target string, content string) error {
	file, openErr := os.OpenFile(target, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, os.ModePerm)
	if openErr != nil {
		return openErr
	}
	writer := bufio.NewWriter(file)
	_, writeErr := writer.WriteString(content)
	if writeErr != nil {
		return writeErr
	}
	defer func() {
		_ = writer.Flush()
	}()
	return nil
}
